REST API en backend
Verschil backend / API
- Wordt veel door elkaar gebruikt in deze cursus
- Niet fout vanuit frontend perspectief
- Tekening om het verschil duidelijk te maken
Frontend / Backend vs Clientside / Serverside
- Eigenlijk is clientside / serverside gepastere benaming voor wat er echt gebeurd
- Client = het aparaat dat je gebruikt om de web applicatie te bezoeken
- Server = computer in de "the cloud" waar de data leeft
Aan beide kanten kan code runnen Deze twee kanten communiceren met elkaar door middel van een afgesproken contract = API
Verschil in taalgebruik
Client
- beperktere keuze = JS, HTML en CSS zijn in wezen de enige talen die het begrijpt
- SVG, JSON, bepaalde videoformaten kan ook maar het runnen en weergeven van webpaginas door die 3
- Nuance verschil door WASM
- Er zijn ook verschillende browser apis die door javascript gebruikt kunnen worden maar er is nog steeds JS nodig
WASM (small sidetrack)
wasm home page WebAssembly is een binair instructieformaat dat werkt als een soort virtuele machine in de browser. In simpele termen:
- Het is een manier om code die in andere talen (zoals C, C++, Rust) is geschreven snel in browsers te laten draaien
- Het werkt als een soort "vertaler" die deze code omzet naar iets wat browsers begrijpen
- Het hoofddoel is betere prestaties dan JavaScript voor rekenintensieve taken
- Het werkt samen met JavaScript, niet als vervanging ervan
Server
- Een wijde varieteit aan talen:
- Node.js (JavaScript) - Zeer populair vanwege de mogelijkheid om dezelfde taal aan zowel client- als server-kant te gebruiken.
const axios = require('axios');
async function getUsers() {
try {
const response = await axios.get('https://api.example.com/users');
console.log('Gebruikers:', response.data);
return response.data;
} catch (error) {
console.error('Fout bij het ophalen van gebruikers:', error.message);
throw error;
}
}
getUsers();
- Python - Bekend om zijn leesbaarheid en veelzijdigheid, vaak gebruikt met frameworks zoals Django en Flask.
import requests
def get_users():
try:
response = requests.get('https://api.example.com/users')
response.raise_for_status() # Controleert of het verzoek succesvol was
users = response.json()
print(f"Gebruikers: {users}")
return users
except requests.exceptions.RequestException as e:
print(f"Fout bij het ophalen van gebruikers: {e}")
raise
if __name__ == "__main__":
get_users()
- PHP - Drijft ongeveer 78% van alle websites aan, inclusief WordPress. Frameworks zoals Laravel en Symfony maken het krachtiger.
<?php
function getUsers() {
$url = 'https://api.example.com/users';
$curl = curl_init($url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($curl);
if($response === false) {
$error = curl_error($curl);
curl_close($curl);
echo "Fout bij het ophalen van gebruikers: " . $error;
return null;
}
curl_close($curl);
$users = json_decode($response, true);
echo "Gebruikers: " . print_r($users, true);
return $users;
}
getUsers();
?>
- Java - Zeer robuust en schaalbaar, populair voor enterprise-toepassingen met frameworks zoals Spring.
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers;
public class RestApiExample {
public static void main(String[] args) {
try {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com/users"))
.GET()
.build();
HttpResponse<String> response = client.send(request, BodyHandlers.ofString());
System.out.println("Statuscode: " + response.statusCode());
System.out.println("Gebruikers: " + response.body());
} catch (Exception e) {
System.out.println("Fout bij het ophalen van gebruikers: " + e.getMessage());
}
}
}
- C# (.NET) - Microsoft's taal die vaak wordt gebruikt voor Windows-servers en steeds meer cross-platform met .NET Core.
using System;
using System.Net.Http;
using System.Threading.Tasks;
using System.Text.Json;
class Program
{
static async Task Main(string[] args)
{
await GetUsers();
}
static async Task GetUsers()
{
try
{
using (HttpClient client = new HttpClient())
{
HttpResponseMessage response = await client.GetAsync("https://api.example.com/users");
response.EnsureSuccessStatusCode();
string responseBody = await response.Content.ReadAsStringAsync();
var users = JsonSerializer.Deserialize<object>(responseBody);
Console.WriteLine($"Gebruikers: {responseBody}");
}
}
catch (Exception ex)
{
Console.WriteLine($"Fout bij het ophalen van gebruikers: {ex.Message}");
}
}
}
- Ruby - Bekend om Ruby on Rails, een framework dat productiviteit en snelle ontwikkeling bevordert.
require 'net/http'
require 'json'
def get_users
begin
uri = URI('https://api.example.com/users')
response = Net::HTTP.get_response(uri)
if response.is_a?(Net::HTTPSuccess)
users = JSON.parse(response.body)
puts "Gebruikers: #{users}"
return users
else
puts "Fout bij het ophalen van gebruikers: #{response.code} #{response.message}"
return nil
end
rescue => e
puts "Fout bij het ophalen van gebruikers: #{e.message}"
raise
end
end
get_users
- Go (Golang) - Ontwikkeld door Google, gewaardeerd om zijn snelheid en eenvoud, vooral voor microservices.
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
)
func main() {
getUsers()
}
func getUsers() {
resp, err := http.Get("https://api.example.com/users")
if err != nil {
fmt.Println("Fout bij het ophalen van gebruikers:", err)
return
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
fmt.Println("Fout bij het ophalen van gebruikers. Statuscode:", resp.StatusCode)
return
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println("Fout bij het lezen van de response:", err)
return
}
var users interface{}
err = json.Unmarshal(body, &users)
if err != nil {
fmt.Println("Fout bij het verwerken van JSON:", err)
return
}
fmt.Println("Gebruikers:", users)
}
- Rust - Groeiende populariteit vanwege veiligheid en prestaties, vooral voor systemen waar betrouwbaarheid cruciaal is.
use reqwest;
use serde_json::Value;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
match get_users().await {
Ok(users) => println!("Gebruikers: {:?}", users),
Err(e) => println!("Fout bij het ophalen van gebruikers: {}", e),
}
Ok(())
}
async fn get_users() -> Result<Value, Box<dyn std::error::Error>> {
let client = reqwest::Client::new();
let response = client.get("https://api.example.com/users")
.send()
.await?;
if response.status().is_success() {
let users: Value = response.json().await?;
Ok(users)
} else {
Err(format!("API fout: {}", response.status()).into())
}
}
De keuze hangt vaak af van factoren zoals het type project, beschikbare expertise in het team, prestatie-eisen en het ecosysteem van frameworks en bibliotheken.
Om REST API's uit te leggen aan beginners met je Node.js/Express applicatie als voorbeeld, zou ik deze aanpak aanbevelen:
Basisconcepten
Begin met een eenvoudige uitleg van wat een API is:
- "Een API (Application Programming Interface) is zoals een ober in een restaurant - hij neemt je bestelling aan, geeft deze door aan de keuken, en brengt je eten terug"
- "Een REST API gebruikt het internet om gegevens tussen verschillende systemen uit te wisselen via standaard HTTP-verzoeken (GET, POST, PUT, DELETE)"
Klein schema overzicht
Teken een eenvoudig schema dat laat zien hoe de verschillende onderdelen samenhangen:
[Client] <--HTTP verzoek--> [Express Server]
|
[Controllers --> Services --> Repositories]
|
[Database]
Project structuur
-
Controllers (de verkeerleiders):
- "Controllers ontvangen verzoeken en sturen antwoorden"
- Voorbeeld:
groupController.jsontvangt een verzoek om iemand uit te nodigen voor een groep
-
Services (de bedrijfslogica):
- "Services bevatten de bedrijfslogica - de regels en bewerkingen"
- Voorbeeld:
groupService.jscontroleert of de gebruiker iemand kan uitnodigen voor die groep
-
Repositories (databeheer):
- "Repositories praten met de database"
- Voorbeeld:
groupResository.jsslaat de uitnodiging op
Show and tell
Stukje uit de code -> Tonen van het uitsturen van een invitation voor een group
HTTP-methoden uit / CRUD acties
- GET: Gegevens ophalen (Read)
- POST: Nieuwe gegevens maken (Create)
- PUT/PATCH: Gegevens bijwerken (Update)
- DELETE: Gegevens verwijderen (Delete)
Fundamentele REST-concepten (REpresentational State Transfer)
RESTful principes**: Stateless, resource-georiënteerd, uniforme interface
Stateless (Toestandloos)
"Stateless" betekent dat de server geen geheugen heeft van vorige verzoeken.
- Eenvoudige uitleg: Elke keer dat je de server iets vraagt, moet je je "volledig voorstellen" - de server onthoudt niet wie je bent van een vorig verzoek.
- Voorbeeld: Als je inlogt en daarna een artikel wilt bekijken, moet je bij dat tweede verzoek opnieuw bewijzen wie je bent (meestal met een token of cookie).
- Voordeel: Servers kunnen makkelijker schalen omdat ze geen gebruikersinformatie hoeven te onthouden.
Resource-georiënteerd
REST draait om "resources" (bronnen) - dingen zoals gebruikers, artikelen, producten.
- Eenvoudige uitleg: Je API is georganiseerd rond "dingen" (resources), niet rond "acties".
- Voorbeeld:
- Goed:
/users/123(de resource is een gebruiker) - Minder REST-achtig:
/getUserData?id=123(focus op de actie)
- Goed:
- Voordeel: Logische structuur die intuïtief te begrijpen is.
Uniforme interface
Alle resources worden op dezelfde manier benaderd, volgens vaste regels.
- Eenvoudige uitleg: Het is als een standaard bestelformulier - iedereen gebruikt dezelfde methodes om verschillende dingen te doen.
- Voorbeeld:
- GET voor ophalen
- POST voor aanmaken
- PUT/PATCH voor bijwerken
- DELETE voor verwijderen
- Voordeel: Zodra iemand weet hoe één deel van je API werkt, begrijpen ze grotendeels hoe de rest werkt.
Andere
- HTTP-statuscodes: 200 (OK), 201 (Created), 400 (Bad Request), 404 (Not Found), 500 (Server Error)
- URL-structuur: Hoe je endpoints logisch opbouwt (
/resources/{id}) - Request parameters: Path, query, body, headers
Middleware en JWT
Wat is een JWT?
Een JWT is een lange tekstreeks die er ongeveer zo uitziet:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkphbiBKYW5zZW4iLCJpYXQiOjE1MTYyMzkwMjJ9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Hoe werkt het?
-
Inloggen: Wanneer je inlogt op een website, controleert de server je wachtwoord.
-
Token krijgen: Als alles klopt, geeft de server je een JWT token (dat digitale paspoort).
-
Token bewaren: Je browser bewaart dit token (meestal in local storage of een cookie).
-
Bij elk verzoek: Wanneer je iets wilt doen op de website, stuurt je browser het token mee: "Hallo, ik ben het weer, hier is mijn paspoort."
-
Verificatie: De server bekijkt het token en kan zien:
- Wie je bent
- Welke rechten je hebt
- Of het token nog geldig is
Onderdelen van een JWT
Een JWT bestaat uit drie delen, gescheiden door punten:
- Header: Informatie over wat voor soort token het is
- Payload: De gegevens zelf (zoals je gebruikers-ID, naam, rechten)
- Signature: Een soort digitale handtekening die bewijst dat het token echt is
Waarom worden JWT's gebruikt?
- Stateless: De server hoeft geen sessies bij te houden
- Veilig: Het token kan niet worden vervalst (dankzij de handtekening)
Data & Validatie
- Data validatie: Waarom validatie cruciaal is en hoe implementeren
- Schema validatie: Tools zoals Joi, Yup of Zod
- Sanitization: Hoe je data veilig maakt voordat je het gebruikt
Database interactie
- ORM/ODM concepten: Hoe je database-operaties abstraheert
- Transactions: Waarom ze belangrijk zijn voor data-integriteit
- Migrations: Hoe je database-schema's veilig aanpast
API Design
- Versioning: Waarom je API's moet versionen (v1, v2, etc.)
- Pagination: Hoe je grote datasets efficiënt serveert
- Filtering, Sorting & Searching: Implementatie-strategieën
Error Handling
- Consistente error responses: Hoe maak je uniforme error-berichten
- Logging: Het belang van goede logging voor debugging
- Try-catch patronen: Hoe vang je fouten netjes af
Performance & Schaling
- Caching: Implementatie en strategieën
- Rate limiting: Beschermen tegen overbelasting
Documentation
- API documentatie: OpenAPI/Swagger
Deployment & DevOps
- Environment variables: Configuratie en geheimen beheren